import type { DatePickerProps, ImageProps, SwitchProps, TimePickerProps, UploadProps } from 'naive-ui'
import type { PropType } from 'vue'
import type { RenderViewProps } from '@/context'
import type { NvOption, RowData } from '@/types'
import { NFlex, NIcon, NImage, NImageGroup, NSwitch, NTag, NTime, NUpload, useThemeVars } from 'naive-ui'
import { useLocale } from 'naive-ui/es/_mixins'
import { strictParse } from 'naive-ui/es/date-picker/src/utils'
import { renderDocumentIcon, renderImageIcon } from 'naive-ui/es/upload/src/icons'
import { isImageFile } from 'naive-ui/es/upload/src/utils'
import { defineComponent } from 'vue'
import './render-view.scss'

export const defaultRenderView = defineComponent({
  props: ['value'],
  render() {
    return <span>{this.value}</span>
  },
})

export const renderSwitchView = defineComponent({
  inheritAttrs: false,
  props: {
    value: { type: [String, Number, Boolean], default: false },
    checkedValue: { type: Object as PropType<SwitchProps['checkedValue']> },
    uncheckedValue: { type: Object as PropType<SwitchProps['uncheckedValue']> },
    size: { type: String as PropType<SwitchProps['size']> },
    round: { type: Boolean },
    railStyle: { type: Object as PropType<SwitchProps['railStyle']> },
  },
  render() {
    return (
      <span>
        <NSwitch {...this.$props} rubberBand={false} class="nv-crud--view">
          {{ ...this.$slots }}
        </NSwitch>
      </span>
    )
  },
})

export const renderDictView = defineComponent({
  inheritAttrs: false,
  props: ['value', 'dictData', 'size', 'renderTag', 'multiple', 'showPath', 'separator', 'labelField', 'valueField', 'childrenField'],
  setup(props) {
    const findDfs = (options?: NvOption[], val?: any): NvOption[] => {
      const childrenField = props.childrenField || 'children'
      if (!options?.length || val === undefined || val === null) {
        return []
      }

      for (const option of options) {
        if (option[props.valueField || 'value'] === val) {
          return [option]
        }
        else if (Array.isArray(option[childrenField])) {
          const target = findDfs(option[childrenField] as any, val)
          if (target.length) {
            return [option, ...target]
          }
        }
      }

      return []
    }

    return () => {
      const labelField = props.labelField || 'label'
      const valueField = props.valueField || 'value'
      const values = Array.isArray(props.value) ? props.value : [props.value]
      if (props.multiple) {
        // 渲染为tag
        const contents = values.map((val) => {
          const optionChain = findDfs(props.dictData, val)
          const lastOption = optionChain[optionChain.length - 1] || { label: `${val}`, value: val, [labelField]: `${val}`, [valueField]: val }

          if (props.renderTag) {
            return <renderTag option={lastOption} handleClose={() => {}} class="nv-crud--view" />
          }
          else {
            let displayText = ''
            if (props.showPath) {
              displayText = optionChain.map(option => option[labelField]).join(props.separator || ' / ')
            }
            else {
              displayText = (lastOption[labelField] || val) as string
            }
            return <NTag size={props.size} class="nv-crud--view">{displayText}</NTag>
          }
        })
        return (
          <span class="nv-crud-cell--no-ellipsis">
            {contents}
          </span>
        )
      }
      else {
        // 渲染为文字
        const content = values.map((val) => {
          const optionChain = findDfs(props.dictData, val)
          if (props.showPath) {
            return optionChain.map(option => option[labelField]).join(props.separator)
          }
          else {
            return ([...optionChain].pop()?.[labelField] || val) as string
          }
        }).join('、')

        return content
      }
    }
  },
})

const imgIconBase64 = 'data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'1em\' height=\'1em\' viewBox=\'0 0 24 24\'%3E%3C!-- Icon from All by undefined - undefined --%3E%3Cpath fill=\'rgb(189 189 189)\' d=\'M5 21q-.825 0-1.412-.587T3 19V5q0-.825.588-1.412T5 3h14q.825 0 1.413.588T21 5v14q0 .825-.587 1.413T19 21zm1-8.425l4-4l4 4l4-4l1 1V5H5v6.575zM5 19h14v-6.6l-1-1l-4 4l-4-4l-4 4l-1-1zm0 0v-6.6v2V5z\'/%3E%3C/svg%3E'
const docIconBase64 = 'data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'1em\' height=\'1em\' viewBox=\'0 0 24 24\'%3E%3C!-- Icon from Google Material Icons by Material Design Authors - https://github.com/material-icons/material-icons/blob/master/LICENSE --%3E%3Cpath fill=\'rgb(189 189 189)\' d=\'M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8zM6 20V4h7v5h5v11z\'/%3E%3C/svg%3E'

interface ImageGroupProps { fileList: UploadProps['fileList'], size: number, iconSize: number, mode?: RenderViewProps['mode'] }
function ImageGroup({ fileList, size, iconSize, mode }: ImageGroupProps) {
  const theme = useThemeVars()
  const imageItems = fileList?.map((fileItem) => {
    const imageProps: ImageProps & RowData = {
      src: fileItem.thumbnailUrl || fileItem.url! || 'error',
      width: size,
      height: size,
      title: fileItem.name,
    }
    if (mode === 'cell') {
      const fallbackSrc = isImageFile(fileItem as any) ? imgIconBase64 : docIconBase64
      imageProps.fallbackSrc = fallbackSrc
      imageProps.style = { marginRight: '5px' }
      return <NImage {...imageProps} />
    }
    else {
      const renderErrorIcon = isImageFile(fileItem as any) ? renderImageIcon : renderDocumentIcon
      imageProps.style = {
        width: `${size}px`,
        height: `${size}px`,
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        border: `1px solid ${theme.value.borderColor}`,
      }

      return (
        <NImage {...imageProps}>
          {{ error: () => <NIcon size={iconSize} color="lightGrey">{renderErrorIcon}</NIcon> }}
        </NImage>
      )
    }
  })
  return (
    <span>
      <NImageGroup>
        { mode === 'cell' ? imageItems : <NFlex style="gap: 8px">{imageItems}</NFlex> }
      </NImageGroup>
    </span>
  )
}
interface UploadViewProps extends RenderViewProps {
  fileList?: UploadProps['fileList']
  listType?: UploadProps['listType']
}
export function renderUploadView({ fileList, listType, mode }: UploadViewProps) {
  if (mode === 'cell') {
    if (!listType || listType === 'text') {
      const displayText = fileList?.map((fileItem, idx) => {
        return [
          idx > 0 ? '、' : '',
          <a href={fileItem.url!} target="_blank">{fileItem.name}</a>,
        ]
      }).flat()

      return displayText
    }
    else {
      return <ImageGroup fileList={fileList} size={28} iconSize={28} mode={mode} />
    }
  }
  else {
    if (listType === 'image-card') {
      return <ImageGroup fileList={fileList} size={94} iconSize={36} />
    }
    else {
      return (
        <div style="width: 100%">
          <NUpload class="nv-crud--view" defaultFileList={fileList} listType={listType} />
        </div>
      )
    }
  }
}

interface DateViewProps extends RenderViewProps {
  formattedValue?: DatePickerProps['formattedValue']
  format?: DatePickerProps['format']
  valueFormat?: DatePickerProps['valueFormat']
  type?: DatePickerProps['type']
}
export function renderDateView({ formattedValue, format: displayFormat, valueFormat, type }: DateViewProps) {
  if (!formattedValue)
    return ''

  const { localeRef, dateLocaleRef } = useLocale('DatePicker')
  function getValueFormat() {
    if (valueFormat)
      return valueFormat
    if (displayFormat)
      return displayFormat
    switch (type) {
      case 'date':
      case 'daterange':
        return localeRef.value.dateFormat
      case 'datetime':
      case 'datetimerange':
        return localeRef.value.dateTimeFormat
      case 'year':
      case 'yearrange':
        return localeRef.value.yearTypeFormat
      case 'month':
      case 'monthrange':
        return localeRef.value.monthTypeFormat
      case 'quarter':
      case 'quarterrange':
        return localeRef.value.quarterFormat
      case 'week':
        return localeRef.value.weekFormat
    }
  }

  valueFormat = getValueFormat()!
  displayFormat = displayFormat || valueFormat
  const dateFnsOptions = {
    locale: dateLocaleRef.value.locale,
    useAdditionalWeekYearTokens: true,
  }
  const isRange = type?.endsWith('range') ?? false

  if (isRange) {
    const [start, end] = formattedValue as [string, string]
    const startTimestamp = strictParse(start, valueFormat!, new Date(), dateFnsOptions).getTime()
    const endTimestamp = strictParse(end, valueFormat!, new Date(), dateFnsOptions).getTime()
    return (
      <span>
        <NTime time={startTimestamp} format={displayFormat} />
        {' ~ '}
        <NTime time={endTimestamp} format={displayFormat} />
      </span>
    )
  }
  else {
    const timestamp = strictParse(formattedValue as string, valueFormat!, new Date(), dateFnsOptions).getTime()
    return (
      <span>
        <NTime time={timestamp} format={displayFormat} />
      </span>
    )
  }
}

interface TimeViewProps extends RenderViewProps {
  formattedValue?: TimePickerProps['formattedValue']
  format?: TimePickerProps['format']
  valueFormat?: TimePickerProps['valueFormat']
  timeZone?: TimePickerProps['timeZone']
}
export function renderTimeView({ formattedValue, format: displayFormat = 'HH:mm:ss', valueFormat, timeZone }: TimeViewProps) {
  if (!formattedValue)
    return ''

  const { dateLocaleRef } = useLocale('TimePicker')

  const timestamp = strictParse(
    formattedValue,
    valueFormat || displayFormat!,
    new Date(),
    { locale: dateLocaleRef.value.locale },
  ).getTime()

  return (
    <span>
      <NTime time={timestamp} format={displayFormat} timeZone={timeZone} />
    </span>
  )
}
